package com.blog.service.impl;


import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.blog.common.Result.PageResult;
import com.blog.common.basecontext.BaseContext;
import com.blog.common.exception.BaseException;
import com.blog.common.exception.LoginFailedException;
import com.blog.dto.ArticleAddDto;
import com.blog.dto.ArticleUpdateDto;
import com.blog.entity.Article;
import com.blog.entity.User;
import com.blog.mapper.ArticleMapper;
import com.blog.service.IArticleService;
import com.blog.service.IUserService;
import com.blog.vo.ArticleGetByDateVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.java.Log;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.security.auth.login.LoginException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements IArticleService {

    private final IUserService userService;

    private final RestHighLevelClient restHighLevelClient;

    /**
     * 根据日期查询文章列表
     *
     * @return
     */
    @Override
    public List<ArticleGetByDateVo> getListByDate() {
        //查询数据库中的列表
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByDesc(Article::getCreateTime);
        List<Article> articleList = list(queryWrapper);
        // 获得数据后进行复制
        List<ArticleGetByDateVo> articleGetByDateVoList = articleList.stream().map(article -> {
            ArticleGetByDateVo articleGetByDateVo = new ArticleGetByDateVo();
            BeanUtils.copyProperties(article, articleGetByDateVo);
            return articleGetByDateVo;
        }).toList();
        return articleGetByDateVoList;
    }


    /**
     * 添加文章
     *
     * @param articleAddDto
     */
    @Override
    public void addArticle(ArticleAddDto articleAddDto) {
        // 获取当前登录用户
        Long currentId = BaseContext.getCurrentId();
        // 查询用户
        User currentUser = userService.getById(currentId);
        if (currentUser == null) {
            throw new LoginFailedException("用户不存在");
        }
        // 将数据复制到实体类
        Article article = new Article();
        BeanUtils.copyProperties(articleAddDto, article);
        article.setUserId(currentId);
        article.setAuthorName(currentUser.getName());
        // 保存到数据库
        save(article);
//        System.out.println(article);
        //添加到es中
        ArticleGetByDateVo articleGetByDateVo = new ArticleGetByDateVo();
        BeanUtils.copyProperties(article, articleGetByDateVo);
        IndexRequest request = new IndexRequest("article").id(article.getId().toString()); //指定索引名和id
        request.source(JSON.toJSONString(articleGetByDateVo), XContentType.JSON); //指定数据内容和类型
        try {
            restHighLevelClient.index(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除文章
     *
     * @param id
     */
    @Override
    public void deleteArticle(Long id) throws IOException {
        // 查询当前登录用户id
        Long currentId = BaseContext.getCurrentId();
        // 查询文章
        Article article = getById(id);
        // 判断该文章是否为当前用户创建
        if (!article.getUserId().equals(currentId)) {
            throw new BaseException("该文章不是当前用户创建");
        }
        // 删除文章
        removeById(id);
        // 同时删除es中的数据
        restHighLevelClient.delete(new DeleteRequest("article").id(id.toString()), RequestOptions.DEFAULT);
    }

    /**
     * 更新文章
     *
     * @param articleUpdateDto
     */
    @Override
    public void updateArticle(ArticleUpdateDto articleUpdateDto) throws IOException {
        // 查询当前登录用户id
        Long currentId = BaseContext.getCurrentId();
        // 查询文章
        Article article = getById(articleUpdateDto.getId());
        // 判断该文章是否为当前用户创建
        if (!article.getUserId().equals(currentId)) {
            throw new BaseException("该文章不是当前用户创建");
        }
        // 将数据复制到实体类
        BeanUtils.copyProperties(articleUpdateDto, article);
        // 更新到数据库
        updateById(article);
        // 同时更新es中的数据
        ArticleGetByDateVo articleGetByDateVo = new ArticleGetByDateVo();
        // 复制属性
        BeanUtils.copyProperties(article, articleGetByDateVo);
        IndexRequest request = new IndexRequest("article").id(article.getId().toString()); //指定索引名和id
        request.source(JSON.toJSONString(articleGetByDateVo), XContentType.JSON); //指定数据内容和类型
        restHighLevelClient.index(request, RequestOptions.DEFAULT); //执行更新操作
    }


    /**
     * 根据用户id查询文章列表
     *
     * @param userId
     * @return
     */
    @Override
    public PageResult getArticlesByUserId(Long userId, Integer pageNum, Integer pageSize) {
        LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Article::getUserId, userId);
        Page<Article> page = new Page<>(pageNum, pageSize);
        queryWrapper.orderByDesc(Article::getCreateTime);
        Page<Article> result = page(page, queryWrapper);
        return new PageResult(result.getTotal(), result.getRecords());
    }

    @Override
    public PageResult getArticleByTitle(String name, Integer pageNum, Integer pageSize) throws IOException {
        if (pageNum <= 0){
            throw new BaseException("页数不能小于等于0");
        }
        SearchRequest request = new SearchRequest("article");
        request.source().query(QueryBuilders.matchQuery("title", name)); //指定查询条件
        request.source().from((pageNum - 1) * pageSize).size(pageSize); //指定分页参数
        request.source().highlighter(new HighlightBuilder().field("title")); //指定高亮字段
        SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        // 获取查询结果
        SearchHits searchHits = response.getHits();
        // 总条数
        PageResult pageResult = new PageResult();
        long total = searchHits.getTotalHits().value;
        pageResult.setTotal(total);
        // 获取查询结果列表
        SearchHit[] hits = searchHits.getHits();
        List<ArticleGetByDateVo> articleList = new ArrayList<>();
        for (SearchHit hit : hits) {
            // 获取source结果
            String json = hit.getSourceAsString();
            // 将json转换为对象
            ArticleGetByDateVo articleGetByDateVo = JSONUtil.toBean(json, ArticleGetByDateVo.class);
//            System.out.println(articleGetByDateVo);
            // 获取高亮字段
            Map<String, HighlightField> fields = hit.getHighlightFields();
            if (fields != null && !fields.isEmpty()) {
                //根据字段名获取高亮字段
                HighlightField field = fields.get("title");
                // 获取高亮内容，覆盖之前
                String hfName = field.fragments()[0].toString();
                articleGetByDateVo.setTitle(hfName); // 覆盖之前的内容
            }
            articleList.add(articleGetByDateVo);
        }

        pageResult.setRecords(articleList);
        return pageResult;
    }

}
